home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
DISK
/
CSAP312.ARJ
/
CSAP.C
next >
Wrap
C/C++ Source or Header
|
1992-02-16
|
17KB
|
553 lines
/*
**************************** NOTICE! **************************
* Contrary to the current trend in MS-DOS software this *
* program, for whatever it is worth, is NOT copyrighted *
* (with the exception of the runtime library from Borland *
* International's Turbo C)! The program, in whole or in *
* part, may be used freely in any fashion or environment *
* desired. If you find this program to be useful to you, *
* do NOT send any contribution to the author; in the words *
* of Rick Conn, 'Enjoy!' However, if you make any *
* improvements, I would enjoy receiving a copy of the *
* modified source. I can be reached, usually within 24 *
* hours, by messages on any of the Phoenix systems, *
* particularly: *
* *
* Technoids Anonymous [PCBOARD] *
* (602) 899-4876 300/1200/2400 bps *
* *
* or: *
* on CompuServ: 70410,1004 *
* or: *
* Don A. Williams *
* 3913 W. Solano Dr. N. *
* Phoenix, AZ 85019 *
* (602) 841-5333 *
* *
* Every effort has been made to avoid error and moderately *
* extensive testing has been performed on this program, *
* however, the author does not warrant it to be fit for any *
* purpose or to be free from error and disclaims any *
* liability for actual or any other damage arising from the *
* use of this program. *
*****************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include <ctype.h>
#include <dir.h>
#include <mem.h>
#include <alloc.h>
#include "dosstruc.h"
/*--- Function Prototypes ---*/
void Usage (void);
void GetDPB (int Disk, struct DpbStruct * Dpb);
unsigned GetDosVersion (void);
char *strrspn (char *s1, char *s2);
int AbortProgram (void);
void SortDir (void);
/*--- End of Prototypes ---*/
#define MAX12BIT 0x0FF6
#define MAX16BIT 0xFFF6
#if defined(__TINY__)
#define MODEL "Tiny"
#elif defined(__SMALL__)
#define MODEL "Small"
#elif defined(__COMPACT__)
#define MODEL "Compact"
#elif defined(__MEDIUM__)
#define MODEL "Medium"
#elif defined(__LARGE__)
#define MODEL "Large"
#elif defined(__HUGE__)
#define MODEL "Huge"
#endif
char Disk; /* Alpha working disk ('A', 'B', .... ) */
char CurDir[67]; /* Storage for Current Directory of disk */
char Path[67]; /* Storage for Path to sort */
char Parent[67]; /* Storage for Parent part of Path */
char Element[13]; /* Storage for Child part of Path */
char Line[80]; /* Working storage for strings */
char Order = 'N'; /* Sort key indicator (Default=Name/Ext) */
char Inverse = 0; /* Ascend/Descend indic. (Default=Ascend) */
char Level = 0; /* Recursive sort indic. (default=Recursive) */
char RSwt = 0; /* Report switch (Default=No Report) */
char VerSwt = 0; /* Pause for operator verify (Default=off) */
char Packed = 1; /* Elim. "erased" entries (Default=on) */
char TruncateSwt = 0;/* Truncate directories (Default=off) */
unsigned char *Fat; /* Pointer to FAT buffer (dynamic) */
char FatDirty = 0; /* FAT needs to be rewritten */
int Is12Bit; /* 12 / 16 bit cluster indicator */
int *CluArray; /* Cluster Array ptr, dynamically allocated */
int Lim, i, j, k, l;
int OutSectors, OutClusters, BytesPerCluster, ECount;
unsigned LastCluster; /* Value for end of cluster chain */
unsigned Cluster, Sector, NumSec;
unsigned FatSize;
unsigned Version;
long MinMem; /* Minimum available memory */
unsigned Freed = 0; /* Freed cluster count */
unsigned Version;
unsigned FatSize;
unsigned DirStart;
struct DpbStruct Dpb; /* Disk Parameter Block (see dosstruc.h) */
struct ClusterQueue CluQ; /* Queue of cluster for directory */
struct DirEntry *DirBuff; /* Buffer for directory to be sorted */
struct ExtendedEntry Dir;
struct ClusterEntry *p, *t;
struct ExtFcb Fcb;
void
main (int argc, char *argv[]) {
char *strrspn();
void SortDir(), Usage();
char *p, *p1, t1;
int i, j;
bdos(0x0D, 0, 0); /* Reset Disk Subsystem - Flush all buffers */
fputs("C-Sort And Pack [CSAP]: Version 3.1.2: Date: December 16, 1991", stderr);
fputs(" [", stderr);
fputs(MODEL, stderr);
fputs(" Model]\n", stderr);
fputs(" use \"CSAP -H\" or \"CSAP ?\" for help.\n\n", stderr);
Disk = getdisk() + 'A';
Line[0] = '\\';
getcurdir(Disk - '@', &Line[1]);
ctrlbrk(AbortProgram); /* Install "wrap-up" in Control Break vec. */
/* Interpret command line arguments, if any */
for (i = 1; i < argc; ++i) {
if (argv[i][0] == '-') {
for (j = 1; j < strlen(argv[i]); ++j) {
switch (toupper(argv[i][j])) {
case 'N': /* Sort Key = Name/Ext (default) */
Order = 'N';
break;
case 'D': /* Sort Key = Date/Time */
Order = 'D';
break;
case 'E': /* Sort Key = Ext/Name */
Order = 'E';
break;
case 'S': /* Sort Key = File Size */
Order = 'S';
break;
case 'R': /* Report Dir loc. & "erased" */
RSwt = 1;
break;
case 'I': /* Sort order inverse */
Inverse = 1;
break;
case 'P': /* Do NOT remove "erased" entries */
Packed = 0;
break;
case 'L': /* Limit sort to one level */
Level = 1;
break;
case 'V': /* Request approval before sort */
VerSwt = 1;
break;
case 'T': /* Truncate directories */
TruncateSwt = 1;
break;
case 'H':
Usage();
default: /* Illegal option */
fprintf(stderr, "Invalid option %s.\n", argv[i]);
Usage();
break;
}
}
}
else { /* Not switch, assume directory name or '?' */
if (argv[i][0] == '?') Usage();
if (argv[i][1] == ':') { /* Check for disk specified */
Disk = toupper(argv[i][0]);
p = &argv[i][2];
}
else p = &argv[i][0];
if ((p[0] != '\\') && (p[0] != '/')) {
Line[0] = '\\';
getcurdir(Disk - '@', &Line[1]);
p1 = &p[strcspn(p, "\\/")];
t1 = *p1;
*p1 = '\0';
if (!strcmp(p, ".")) {
p = p1;
*p1 = t1;
}
else if (!strcmp(p, "..")) {
while (!strcmp(p, "..")) {
p = strrspn(Line, "\\/");
if ((p - Line) == 0) ++p;
*p = '\0';
if (t1 != '\0') p = ++p1;
else p = p1;
p1 = &p[strcspn(p, "\\/")];
t1 = *p1;
*p1 = '\0';
}
*p1 = t1;
}
else *p1 = t1;
if (*p != '\0') strcat(Line, "\\");
strcat(Line, p);
}
else strcpy(Line, p);
}
}
/*
* Get disk information - uses un-documented DOS call, Int 21H, Func. 32H
* This function has been verified to work correctly in PC/MS-DOS
* versions 2.0 through 3.3. It is heavily used by DOS programs such as
* CHKDSK.
*/
GetDPB(Disk, &Dpb);
Version = GetDosVersion() & 0xFF;
switch (Version) {
case 2:
FatSize = Dpb.V.V2.FatSize;
DirStart = Dpb.V.V2.DirStart;
break;
case 3:
FatSize = Dpb.V.V3.FatSize;
DirStart = Dpb.V.V3.DirStart;
break;
case 4:
case 5:
FatSize = Dpb.V.V4.FatSize;
DirStart = Dpb.V.V4.DirStart;
break;
default:
fprintf(stderr, "Invalid DOS version: %d\n", Version);
exit(1);
}
/* Establish whether disk has 16-bit or 12-bit clusters */
if (Dpb.LastCluster > MAX16BIT) {
fprintf(stderr, "Sorry, CSAP does not yet support FAT entries > 16 bits.\n");
exit(1);
}
Is12Bit = (Dpb.LastCluster > MAX12BIT) ? 0 : 1;
LastCluster = (Is12Bit) ? 0x0FF8 : 0xFFF8;
/*
* Get & save current directory of working disk. We have to change to
* sort and must restore on termination
*/
CurDir[0] = Disk;
CurDir[1] = ':';
CurDir[2] = '\\';
getcurdir(Disk - '@', (char *) &CurDir[3]);
/* Allocate space to hold entire FAT in memory and read it in */
if ((Fat = malloc(FatSize * Dpb.SectorSize)) == NULL) {
fprintf(stderr, "Insufficient memory for FAT.\n");
exit(1);
}
if (absread(Disk - 'A', FatSize, Dpb.FatStart, Fat) != 0) {
fprintf(stderr, "Error reading FAT.\n");
exit(1);
}
/*
* Develop full path name for directory to be sorted and separate into
* Parent and Child portions
*/
Path[0] = Parent[0] = Element[0] = '\0';
Path[0] = Disk;
Path[1] = ':';
Path[2] = '\0';
if ((Line[0] != '\\') && (Line[0] != '/')) {
strcat(Path, "\\");
strcpy(&Path[3], &CurDir[3]);
if ((Path[strlen(Path) - 1] != '\\') && (Path[strlen(Path) - 1] != '/'))
strcat(Path, "\\");
}
strcat(Path, Line);
p = strrspn(Path, "\\/");
strcpy(Element, &p[1]);
if (p[-1] == ':') p++;
strncpy(Parent, Path, (int) (p - Path));
Parent[(int) (p - Path)] = '\0';
MinMem = coreleft(); /* Initialize minimum available memory */
/*
* Perform sort. SortDir is recursive and, if Level is not on, will sort
* sort all levels of the hierarchy from the starting level down
*/
SortDir();
printf("Minimum memory= %ld\n", MinMem);
bdos(0x0D, 0, 0); /* Reset disk subsystem - flush all buffers */
if (FatDirty) {
if (abswrite(Disk - 'A', FatSize, Dpb.FatStart, Fat) != 0) {
fprintf(stderr, "Error writing FAT.\n");
exit(1);
}
if (Dpb.FatCopies == 2) {
if (abswrite(Disk - 'A', FatSize, Dpb.FatStart, Fat) != 0) {
fprintf(stderr, "Error writing 2nd copy of Fat.\n");
exit(1);
}
}
printf("There %s %d cluster%s (%d bytes) freed\n",
(Freed == 1) ? "was" : "were",
Freed,
(Freed == 1) ? "" : "s",
Freed * Dpb.SectorSize * (Dpb.ClusterSize + 1));
}
bdos(0x0D, 0, 0); /* Reset disk subsystem - flush all buffers */
bdosptr(0x3B, CurDir, 0); /* Restore input "current" directory */
} /* end Main */
/*
* STRRSPN is simply a reverse version of STRSPN. It finds the LAST
* occurance in S1 of any member of S2. For some reason, none of the C
* compilers that I use provide this although they all provide STRSPN
*/
char *
strrspn (char *s1, char *s2) {
char *p1;
p1 = s1 + strlen(s1) - 1;
while (p1 >= s1) {
if (strchr(s2, *p1) != NULL) return (p1);
--p1;
}
return ((char *) NULL);
}
/*
* SearchFirst -- Search for First Directory Entry. On entry fcb contains an
* extended File Control Block with file name and attribute bits set. On
* exit, fcb contains matched entry unless return code is 255, in which case
* no match was found. This routine is used instead of the ones provided by
* the caller so that the cluster information for the directory can be
* obtained.
*/
int
SearchFirst (struct ExtFcb * Fcb) {
union REGS regs;
regs.x.ax = 0x1100;
regs.x.dx = (unsigned) Fcb;
intdos(®s, ®s);
return ((int) (regs.x.ax & 0xFF));
}
/*
* Alu2Sec -- Converts an input cluster number [ALU] into the disk-relative
* sector for use with DOS Absolute Disk Read [interrupt 25H] or Absolute
* Disk Write [interrupt 26H]. Requires access to the undocumented DOS Disk
* Parameter Block [use funtion GetDPB].
*/
long
Alu2Sec (struct DpbStruct * Dpb, unsigned Alu) {
return ((long) (Alu - 2) * (Dpb->ClusterSize + 1) + Dpb->DataStart);
}
/*
* NextCl -- This function calculates the logical "chaining" of cluster
* numbers in a File Allocation Table [FAT]. Given an entry cluster number
* it calculates the next cluster using the array Fat[].
*
* If Is12Bit is TRUE then Fat[] is assumed to contain 12 bit entries, otherwise
* Fat[] is assumed to contain 16 bit entries.
*/
unsigned
NextCl (int Is12Bit, unsigned Cluster, unsigned char Fat[]) {
unsigned ClWord, ClOffset;
if (Is12Bit) { /* 12 bit FAT lookup */
ClOffset = 3 * Cluster / 2;
ClWord = Fat[ClOffset] + (Fat[ClOffset + 1] << 8);
if (Cluster & 1) return (ClWord >> 4); /* odd cluster */
else return (ClWord & 0x0FFF); /* even cluster */
}
else return (((unsigned int *) Fat)[Cluster]); /* 16 bit FAT lookup */
}
void
FreeCluster (int Is12Bit, unsigned Val, unsigned Cluster, unsigned char Fat[]) {
extern char FatDirty;
extern unsigned Freed;
unsigned ClWord, ClOffset;
if (Is12Bit) { /* 12 bit FAT lookup */
ClOffset = 3 * Cluster / 2;
ClWord = Fat[ClOffset] + (Fat[ClOffset + 1] << 8);
if (Cluster & 1) ClWord = (ClWord & 0xF) | (Val & 0xFFF0); /* odd */
else ClWord = (ClWord & 0xF000) | (Val & 0x0FFF);
Fat[ClOffset + 1] = ClWord >> 8;
Fat[ClOffset] = ClWord & 0xFF;
}
else ((unsigned int *) Fat)[Cluster] = Val; /* 16 bit FAT lookup */
if (!Val) ++Freed;
}
/*
* PutQueue -- Builds a simple FIFO linked list using dynamically acquired
* memory.
*/
void
PutQueue (struct ClusterQueue * Q, unsigned Cluster) {
struct ClusterEntry *p;
if ((p = malloc(sizeof(struct ClusterEntry))) == NULL) {
fprintf(stderr, "Insufficient memory(1).\n");
AbortProgram();
}
p->Next = NULL;
p->Cluster = Cluster;
if (Q->Head == NULL) Q->Head = p;
else Q->Current->Next = p;
Q->Current = p;
Q->Count++;
}
/*
* AbortProgram -- Aborts the program, resetting the current directory, with
* an error code of 1.
*/
int
AbortProgram (void) {
bdos(0x0D, 0, 0); /* Reset disk subsystem - flush all buffers */
bdosptr(0x3B, CurDir, 0); /* Reset input Current Directory */
exit(1);
return (0);
}
/*
* strincmp -- The comparsion routine for the qsort algorithm.
*/
int
strincmp (struct DirEntry * a, struct DirEntry * b) {
long t;
/*
* Ensure that "erased" entries sort high no matter what the sort key is.
*/
if ((a->Name[0] == 0xE5) && (b->Name[0] != 0xE5)) return (1);
if (b->Name[0] == 0xE5) return (-1);
/*
* Ensure that directories sort lower than files no matter what sort key
*/
if ((a->Name[0] != 0xE5) && (b->Name[0] != 0xE5)) {
if ((a->Attribute & 0x10) ^ (b->Attribute & 0x10)) {
if (a->Attribute & 0x10) return (-1);
else return (1);
}
}
/* Actual sort key compare routines */
switch (Order) {
case 'D': /* Sort key is Date/Time */
if (a->ModifyDate < b->ModifyDate) t = -1;
else if (a->ModifyDate > b->ModifyDate) t = 1;
else {
if (a->ModifyTime < b->ModifyTime) t = -1;
else if (a->ModifyTime > b->ModifyTime) t = 1;
else t = 0;
}
break;
case 'N': /* Sort key is Name/Ext (default) */
t = strncmp((char *) a->Name, (char *) b->Name, 11);
break;
case 'E': /* Sort key is Ext/Name */
if ((t = strncmp(a->Ext, b->Ext, 3)) == 0) {
t = strncmp((char *) a->Name, (char *) b->Name, 8);
}
break;
case 'S': /* Sort key is File Size */
t = a->FileSize - b->FileSize;
break;
default:
t = strncmp((char *) a->Name, (char *) b->Name, 11);
break;
}
if (Inverse) t = -t; /* Sort order is inverse */
return ((t < 0) ? -1 : ((t > 0) ? 1 : 0));
}
unsigned
GetDosVersion (void) {
union REGS Regs;
Regs.h.ah = 0x30;
intdos(&Regs, &Regs);
return (Regs.x.ax);
}
void
Usage (void) {
printf("USAGE: CSAP [options] [[d:]directory_name]\n");
printf(" or\n");
printf(" CSAP [[d:]directory_name] [options]\n");
printf("\n");
printf("Options:\n");
printf(" -N Sort on Name/Ext (default).\n");
printf(" -D Sort on Date/Time.\n");
printf(" -E Sort on Ext/Name.\n");
printf(" -S Sort on File Size.\n");
printf("\n");
printf(" -R Report number of \"erased\" entries and directory location.\n");
printf(" -I Inverse sort order, i.e. descending.\n");
printf(" -P Do NOT remove \"erased\" entries.\n");
printf(" -L Limit sort to a single level.\n");
printf(" -V Request confirmation before sorting.\n");
printf(" -T Truncate directories.\n");
printf(" -H This message.\n");
exit(0);
}